﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace org.xisoft.game.utils
{
    public class ThreadExecuter:IDisposable
    {

        private BlockedQueue<Action> queues = new BlockedQueue<Action>(10240);

        private List<Thread> threads = new List<Thread>();
        private readonly EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);

        private volatile int workingThreadCount;

        public ThreadExecuter()
            :this(Environment.ProcessorCount)
        {

        }
        public ThreadExecuter(int threadCount)
        {
            isRunning = true;
            for (int i = 0; i < threadCount; i++)
            {
                Thread thread = new Thread(Run) { IsBackground = true };
                thread.Start();
                threads.Add(thread);
            }
        }


        private bool isRunning;

        private void Run()
        {
            while(isRunning)
            {
                Action action = queues.Dequeue();
                Interlocked.Increment(ref workingThreadCount);

                action();

                if (Interlocked.Decrement(ref workingThreadCount) == 0)
                {
                    if (isRunning && queues.Count == 0)
                        waitHandle.Set();
                }
            }

        }


        public void Execute(Action action)
        {
            queues.Enqueue(action);
        }

        public void Execute<T>(Func<T> execute,Action<T> callback )
        {
            Action action = () =>
              {
                  T t = execute();
                  callback(t);
              };

            Execute(action);
        }

        public void Dispose()
        {
            isRunning = false;
            for (int i = 0; i < threads.Count; i++)
                Execute(() => { });

            waitHandle.Dispose();
        }

        public bool Idle
        {
            get {
                foreach (Thread t in threads)
                    if ((t.ThreadState & System.Threading.ThreadState.WaitSleepJoin)==0)
                        return false;
                return true;
            }
        }


        public void WaitAllFinish()
        {
            if(queues.Count > 0 || !Idle)
                waitHandle.WaitOne();

        }
    }
}
